/***************************************************************************
 *   oboe.controllers.timeline.TimelineController                          *
 *   Copyright (C) 2011 by Felipe Manga                                    *
 *   lufeboma@gmail.com                                                    *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

package oboe.controllers.timeline
{
	import oboe.core.Oboe;
	import oboe.core.SigSlot;
	import oboe.core.SigSlotCollection;
	import oboe.resources.Files;
	
	public class TimelineController extends SigSlot
	{
		protected var previousGameTime:Number;
		protected var gameTime:Number;
		protected var timeline:Vector.<EventTrigger>;
		protected var lastEvent:int = -1;
		protected var timelinePaused:Boolean;
		private var isPrimaryTimeline:Boolean;
		
		public function TimelineController( timelineFile:String = "", isPrimaryTimeline:Boolean = true )
		{
			super();
			this.gameTime = 0;
			this.timelinePaused = false;
			this.isPrimaryTimeline = isPrimaryTimeline;
			if( timelineFile != "" )
			{
				this.slot_loadTimelineFile( timelineFile );
			}
		}
		
		protected function getTimeline():Object
		{
			return this.timeline;
		}
		
		protected function setTimeline( timeline:Object ):void
		{
			this.timeline = timeline as Vector.<EventTrigger>;
		}
		
		public function slot_loadTimelineFile( file:String ):void
		{
			var data:Array = Files.getFile( file ) as Array;
			this.slot_addTimelineEvents( data );
		}
		
		public function slot_setGameTime( time:Number ):void
		{
			this.previousGameTime = this.gameTime = time;
		}
		
		public function slot_rewindToEvent (eventIndex:int):void
		{
			if (eventIndex < 0 || eventIndex >= timeline.length) return;
			
			lastEvent = eventIndex-1;
			this.slot_setGameTime(timeline[ eventIndex ].time);
		}
		
		private function sortTimeline():void
		{
			if( !timeline ) return;
			timeline.sort(function( a:EventTrigger, b:EventTrigger ):int
			{
				var c:Number = a.time - b.time;
				if( c > 0 ) return 1;
				if( c < 0 ) return -1;
				return 0;
			});
		}
		
		public function slot_addTargetedEvent( time:Number, target:SigSlotCollection, signal:String, ... args ):void
		{
			if( !timeline )
			{
				timeline = new Vector.<EventTrigger>();
			}
			var p:Array = args.slice();
			p.unshift( signal );
			timeline.push( new EventTrigger( time, p, target ) );
			sortTimeline();
		}
		
		public function slot_addTimelineEvent( time:Number, signal:String, ... args ):void
		{
			if( !timeline )
			{
				timeline = new Vector.<EventTrigger>();
			}
			var p:Array = args.slice();
			p.unshift( signal );
			timeline.push( new EventTrigger( time, p ) );
			sortTimeline();
		}
		
		public function slot_addTimelineEvents( data:Array ):void
		{
			data.sortOn("t", Array.NUMERIC);
			if( !timeline )
			{
				timeline = new Vector.<EventTrigger>();
			}
			for each( var event:Object in data )
			{
				var time:Number = event.t;
				var cond:Object;
				var et:EventTrigger;
				cond = null;
				if( "c" in event ) cond = event.c;
				for( var s:String in event )
				{
					if( s == "t" || s == "c" ) continue;
					var param:Array = (event[ s ] as Array).slice();
					param.unshift( s );
					timeline.push( et = new EventTrigger( time, param ) );
					et.condition = cond;
				}
			}
			sortTimeline();
		}
		
		public function slot_pauseTimeline():void
		{
			this.timelinePaused = true;
		}

		public function slot_unpauseTimeline():void
		{
			this.timelinePaused = false;
		}
		
		public function slot_onTick( time:Number ):void
		{
			if( timelinePaused || this.timeline == null ) return;
			
			var et:EventTrigger;
			this.previousGameTime = this.gameTime;
			this.gameTime += time;
			if( this.isPrimaryTimeline ) Oboe.flags["GAME_TIME"] = this.gameTime;
			var continue2:Boolean; // Flash Compiler work around 
			
			if( this.gameTime > this.previousGameTime )
			{
				while( lastEvent < timeline.length-1 && (et = timeline[ lastEvent+1 ]).time <= this.gameTime )
				{
					if( et.condition )
					{
						continue2 = false;
						for( var c:String in et.condition )
						{
							if( Oboe.flags[c] != et.condition[c] )
							{
								lastEvent++;
								continue2 = true;
								break;
							}
						}
						if( continue2 ) continue;
					}
					if( et.target )
					{
						et.target.message.apply( et, timeline[ lastEvent+1 ].signal );
					}
					else
					{
						this.signalUp.apply( null, timeline[ lastEvent+1 ].signal );
					}
					lastEvent++;
					if( timelinePaused ) return;
				}
			}
			else if( this.gameTime < this.previousGameTime )
			{
				while( lastEvent > 0 && (et = timeline[ lastEvent+1 ]).time >= this.gameTime )
				{
					if( et.condition )
					{
						continue2 = false;
						for( var b:String in et.condition )
						{
							if( Oboe.flags[b] != et.condition[b] )
							{
								lastEvent--;
								continue2 = true;
								break;
							}
						}
						if( continue2 ) continue;
					}
					
					if( et.target )
					{
						et.target.message.apply( et, timeline[ lastEvent+1 ].signal );
					}
					else
					{
						this.signalUp.apply( null, timeline[ lastEvent+1 ].signal );
					}
					lastEvent--;
					if( timelinePaused ) return;
				}
			}
		}
	}
}
import oboe.core.SigSlotCollection;

class EventTrigger
{
	public var time:Number;
	public var signal:Array;
	public var target:SigSlotCollection;
	public var condition:Object;
	public function EventTrigger( time:Number, signal:Array, target:SigSlotCollection=null)
	{
		this.time = time;
		this.signal = signal;
		this.target = target;
	}
}